'''
Created on 2011-07-17

@author: workstation
'''
from Tkinter import *
import OSEKitsDefine

class OSEPhotogate(object):
    '''
    Class: OSEPhotogate
    
    Description:
        This class handles the use of the photogate sensor.  Contains functions to parse measurement packets, re-draw
        the display, and configure the sensors.
    
    '''
    _number_of_photogates = 2
    __font = "arial 15"
    __x_padding = 10
    __y_padding = 5
    __display_width = 8
    _current_photogate = 0
    _first_time = 0
    _main_window = None
    
    def __init__(self):
        '''
        Constructor
        '''
        self._photogates_data = []          #(distance_value, time_value, velocity_value)
        self._photogates_display = []       #(distance_label, distance_box, time_label, time_display, velocity_label, velocity_display)
        self.gate_entry = StringVar()
        self.gate_entry.set("2")
        self._number_of_photogates = int(self.gate_entry.get()) 
        return
    
    def start_measurement(self):
        '''
        Function: start_measurement
        
        Description:
            Called when starting to take measurements from the micro-controller. Initializes measurement storage
            and sets appropriate flags and counters.
            
        Returns:
            string  "success" when no errors encountered
                    "<error message>" otherwise
        '''
        for gate in range(0, len(self._photogates_data)):
            self._photogates_data[gate][1].set("        ")
            self._photogates_data[gate][2].set("        ")
        self._current_photogate = 0
        return "success"
    
    def stop_measurement(self):
        '''
        Function: stop_measurement
        
        Description:
            Called when done taking measurements from the micro-controller.
            
        Returns:
            string  "success" when no errors encountered
                    "<error message>" otherwise
        '''
        
        self._current_photogate = 0
        return
            
    def add_measurement(self, measurement_packet):
        '''
        Function: add_measurement
        
        Description: 
            Takes in a measurement packet and converts the packet into usable data. Performs
            calculations on the data to get the velocity measurements and update the display.
        
        Parameters: 
            string  a string containing all characters sent from the micro-controller
            
        Returns:
            string  "success" if no errors encountered
                    "<error message>" otherwise
        '''        
        shift = 0
        measurement_data = 0
        
        if self._current_photogate >= self._number_of_photogates:
            return
            
        for char in measurement_packet:
            measurement_data = measurement_data | ord(char) << 8 * shift
            shift = shift + 1        

        if self._current_photogate > 0:
            time_at_gate = (float(measurement_data) / 1000000) - self._first_time
            velocity_at_gate = (float(self._photogates_data[self._current_photogate][0].get()) / 1000) / (time_at_gate - float(self._photogates_data[self._current_photogate -1][1].get()))
            self._photogates_data[self._current_photogate][1].set(str(time_at_gate)[:self.__display_width])
            self._photogates_data[self._current_photogate][2].set(str(velocity_at_gate)[:self.__display_width])
            
        else:
            self._first_time = float(measurement_data) / 1000000
            self._photogates_data[self._current_photogate][1].set("0.00")
        
        self._current_photogate += 1
        
        return 
    
    
    def setup_display(self, MainWindow, startstopbutton):
        '''
        Function: setup_display
        
        Description:
            Adds all display elements to the main window.
            
        Parameters:
            Tkinter.Tk   The main tk window object where the display will be altered
            Tkinter.Button  The start/stop button from the main window
            
        Returns:
            Nothing
        '''
        self._startstopbutton = startstopbutton
        self._photogates_data = []
        self._photogates_display = []
        
        OSEKitsDefine.CONTROL_ROW = self._number_of_photogates + 1

        
        for gate in range(1, self._number_of_photogates+1):
            distance_value = StringVar()
            distance_value.set("        ")
            velocity_value = StringVar()
            velocity_value.set("        ")
            time_value = StringVar()
            time_value.set("        ")
            
            time_label = Label(MainWindow, text = "Time @ gate #" + str(gate), font = self.__font)
            time_display = Label(MainWindow, background = "white", textvariable = time_value, font = self.__font, width = self.__display_width, justify = LEFT)
            time_label.grid(column = 2, row = OSEKitsDefine.FIRST_DISPLAY_ROW + gate-1, sticky = W, padx = self.__x_padding, pady = self.__y_padding)
            time_display.grid(column = 3, row = OSEKitsDefine.FIRST_DISPLAY_ROW + gate-1, sticky = W, padx = self.__x_padding, pady = self.__y_padding)
            
            if gate > 1:
                distance_label = Label(MainWindow, text = "Distance (mm) from ("+str(gate-1)+"-"+str(gate)+ "):", font = self.__font)
                distance_box = Spinbox(MainWindow, background = "white", increment = 1, repeatdelay = 200, repeatinterval = 200, from_ = 1, to_ = 100, textvariable = distance_value, width = 6, font = self.__font)

                velocity_label = Label(MainWindow, text = "Velocity from ("+str(gate-1)+"-"+str(gate)+ "):", font = self.__font)
                velocity_display = Label(MainWindow, background = "white", textvariable = velocity_value, font = self.__font, width = self.__display_width, justify = LEFT)

                
                distance_label.grid(column = 0, row = OSEKitsDefine.FIRST_DISPLAY_ROW + gate-1, sticky = W, padx = self.__x_padding, pady = self.__y_padding)            
                distance_box.grid(column = 1, row = OSEKitsDefine.FIRST_DISPLAY_ROW + gate-1, sticky = W, padx = self.__x_padding, pady = self.__y_padding)            
                velocity_label.grid(column = 4, row = OSEKitsDefine.FIRST_DISPLAY_ROW + gate-1, sticky = W, padx = self.__x_padding, pady = self.__y_padding)            
                velocity_display.grid(column = 5, row = OSEKitsDefine.FIRST_DISPLAY_ROW + gate-1, sticky = W, padx = self.__x_padding, pady = self.__y_padding)
           
                self._photogates_data.append((distance_value, time_value, velocity_value))
                self._photogates_display.append((distance_label, distance_box, time_label, time_display, velocity_label, velocity_display))

            else:
                self._photogates_data.append((distance_value, time_value, velocity_value))
                self._photogates_display.append((None, None, time_label, time_display, None, None))
                
        
        self.gate_entry_label = Label(MainWindow, text = "# of Gates:", font = self.__font)
        self.gate_entry_box = Spinbox(MainWindow, textvariable = self.gate_entry, background = "white", increment = 1, repeatdelay = 200, repeatinterval = 200, from_ = 2, to_ = 8, width = 2, state = "readonly", command = self.adjustDisplay, font = self.__font)
        
        self.gate_entry_label.grid(column = 1, row = OSEKitsDefine.CONTROL_ROW, sticky = W)
        self.gate_entry_box.grid(column = 2, row = OSEKitsDefine.CONTROL_ROW, sticky = W+S)  
        return
    
    def adjustDisplay(self):
        '''
        Function: adjustDisplay
        
        Description:
            Changes main display to display the appropriate number of gates.
            
        Returns:
            Nothing
        '''
        self._number_of_photogates = int(self.gate_entry.get())
        self.destroy_display(self._main_window)
        self.setup_display(self._main_window, self._startstopbutton)
        self._startstopbutton.grid(column = 0, row = OSEKitsDefine.CONTROL_ROW, sticky =S+W)
        
        return
    
    def destroy_display(self, MainWindow):
        '''
        Function: destroy_display
        
        Description:
            Removes all display elements from the main window.
            
        Parameters:
            Tkinter.Tk   The main tk window object where the display will be altered
        
        Returns:
            Nothing
        '''        
        self.gate_entry_box.destroy()
        self.gate_entry_label.destroy()
        for distance_label, distance_box, time_label, time_display, velocity_label, velocity_display in self._photogates_display:
            if distance_label <> None:
                distance_label.destroy()
            if distance_box <> None:
                distance_box.destroy() 
            time_label.destroy()
            time_display.destroy()
            if velocity_label <> None:
                velocity_label.destroy()
            if velocity_display <> None: 
                velocity_display.destroy()
        
        return
            
            
            